import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { finalize } from "rxjs/operators";
import { ScanningMetrics } from '../config';
import { ErrorHandler } from '../../../utils/error-handler';
import { TranslateService } from '@ngx-translate/core';
import { ScanAllRepoService } from './scanAll.service';
import { OriginCron } from '../../../services/interface';
import { CronScheduleComponent } from "../../cron-schedule/cron-schedule.component";
import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../utils/utils";
import { errorHandler as errorHandFn} from "../../../utils/shared/shared.utils";
import { DatePipe } from "@angular/common";

const SCHEDULE_TYPE_NONE = "None";
@Component({
    selector: 'vulnerability-config',
    templateUrl: './vulnerability-config.component.html',
    styleUrls: ['./vulnerability-config.component.scss', '../registry-config.component.scss']
})
export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
    onGoing: boolean;
    originCron: OriginCron;
    schedule: any;
    onSubmitting: boolean = false;
    openState: boolean = false;
    getLabelCurrent: string;

    @ViewChild(CronScheduleComponent)
    CronScheduleComponent: CronScheduleComponent;
    gettingMetrics: boolean;
    private _scanningMetrics: ScanningMetrics;
    totalWidth: number = 200;
    i18nKeyMap = {
       "Pending": "CONFIG.SCANNING.STATUS.PENDING",
       "Running": "CONFIG.SCANNING.STATUS.RUNNING",
       "Stopped": "CONFIG.SCANNING.STATUS.STOPPED",
       "Error": "CONFIG.SCANNING.STATUS.ERROR",
       "Success": "CONFIG.SCANNING.STATUS.SUCCESS",
       "Scheduled": "CONFIG.SCANNING.STATUS.SCHEDULED"
    };
    private _loopScheduleInterval;
    private _loopManualInterval;
    updatedTimeStr: string;
    onGettingUpdatedTimeStr: boolean;
    hasDefaultScanner: boolean = false;
    constructor(
        private scanningService: ScanAllRepoService,
        private errorHandler: ErrorHandler,
        private translate: TranslateService,
    ) { }

    get scanningMetrics(): ScanningMetrics {
        return this._scanningMetrics;
    }
    set scanningMetrics(metrics: ScanningMetrics) {
        // start looping scheduled metrics
        if (metrics && metrics.ongoing && metrics.isScheduled) {
            if (!this._loopScheduleInterval) {
                this._loopScheduleInterval = setInterval(() => {
                    this.getScheduleMetrics();
                }, 5000);
            }
        }
        // stop looping scheduled metrics
        if (metrics && !metrics.ongoing && metrics.isScheduled) {
            if (this._loopScheduleInterval) {
                clearInterval(this._loopScheduleInterval);
                this._loopScheduleInterval = null;
            }
        }
         // start looping manual metrics
        if (metrics && metrics.ongoing && !metrics.isScheduled) {
            if (!this._loopManualInterval) {
                this._loopManualInterval = setInterval(() => {
                    this.getManualMetrics();
                }, 5000);
            }
        }
        // stop looping manual metrics
        if (metrics && !metrics.ongoing && !metrics.isScheduled) {
            if (this._loopManualInterval) {
                clearInterval(this._loopManualInterval);
                this._loopManualInterval = null;
            }
        }
        this._scanningMetrics = metrics;
    }
    get scanAvailable(): boolean {
        return !this.onSubmitting
            && !this.gettingMetrics
            && !this.isOnScanning()
            && this.hasDefaultScanner;
    }

    getScanText() {
        this.translate.get('CONFIG.SCANNING.SCHEDULE_TO_SCAN_ALL').subscribe((res: string) => {
          this.getLabelCurrent = res;
        });
    }
    getSchedule() {
      this.onGoing = true;
      this.scanningService.getSchedule()
      .pipe(finalize(() => {
        this.onGoing = false;
      }))
      .subscribe(schedule => {
        this.initSchedule(schedule);
      }, error => {
        this.errorHandler.error(error);
      });
    }
    getScanners() {
        this.onGettingUpdatedTimeStr = true;
        this.scanningService.getScanners()
            .subscribe(scanners => {
                let flag = false;
                if (scanners && scanners.length) {
                    scanners.forEach(scanner => {
                        if (scanner.is_default) {
                            flag = true;
                            this.initMetrics();
                            this.getSchedule();
                            this.getScannerMetadata(scanner.uuid);
                        }
                    });
                }
                if (flag) {
                    this.hasDefaultScanner = true;
                }
                if (!flag) {
                    this.onGettingUpdatedTimeStr = false;
                    this.translate.get("SCANNER.NO_DEFAULT_SCANNER")
                       .subscribe(res => {
                          this.errorHandler.warning(res);
                       }
                    );
                }
            }, error => {
                this.onGettingUpdatedTimeStr = false;
            });
    }
    getScannerMetadata(uid: string) {
         this.scanningService.getScannerMetadata(uid)
             .pipe(finalize(() => this.onGettingUpdatedTimeStr = false))
             .subscribe(metadata => {
             if (metadata && metadata.properties) {
                 for (let key in metadata.properties) {
                     if (key === DATABASE_UPDATED_PROPERTY && metadata.properties[key]) {
                         this.updatedTimeStr = new DatePipe('en-us').transform(metadata.properties[key], 'short');
                     }
                 }
             }
         });
    }

      public initSchedule(schedule: any) {
        if (schedule && schedule.schedule !== null) {
          this.schedule = schedule;
          this.originCron = this.schedule.schedule;
        } else {
          this.originCron = {
            type: SCHEDULE_TYPE_NONE,
            cron: ''
          };
        }
      }
    ngOnInit(): void {
      this.getScanText();
      this.getScanners();
    }

    ngOnDestroy() {
        if (this._loopScheduleInterval) {
            clearInterval(this._loopScheduleInterval);
            this._loopScheduleInterval = null;
        }
        if (this._loopManualInterval) {
            clearInterval(this._loopManualInterval);
            this._loopManualInterval = null;
        }
    }
    isOnScanning(): boolean {
        return this.scanningMetrics
               && this.scanningMetrics.ongoing;
    }
    getScheduleMetrics() {
        this.gettingMetrics = true;
        this.scanningService.getScheduleMetrics()
            .pipe(finalize(() => this.gettingMetrics = false))
            .subscribe(response => {
                if (response) {
                    response.isScheduled = true;
                    this.scanningMetrics = response;
                }
            });
    }
    getManualMetrics() {
        this.gettingMetrics = true;
        this.scanningService.getManualMetrics()
            .pipe(finalize(() => this.gettingMetrics = false))
            .subscribe(response => {
                if (response) {
                    response.isScheduled = false;
                    this.scanningMetrics = response;
                }
            });
    }
    initMetrics() {
        // get scheduled metrics first
        this.scanningService.getScheduleMetrics()
            .pipe(finalize(() => this.gettingMetrics = false))
            .subscribe(response => {
                // if scheduled scanning is on going
                if (response && response.ongoing) {
                    response.isScheduled = true;
                    this.scanningMetrics = response;
                } else {
                    this.getManualMetrics();
                }
            },
            error => {
                 this.errorHandler.error(error);
                // if error, get manual metrics
                 this.getManualMetrics();
            });
    }
    getI18nKey(str: string): string {
        if (str && this.i18nKeyMap[str]) {
            return this.i18nKeyMap[str];
        }
        return str;
    }
    errorWidth() {
        if (this.scanningMetrics
            && this.scanningMetrics.metrics
            && this.scanningMetrics.total
            && this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.ERROR]) {
            return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.ERROR] /
                this.scanningMetrics.total * this.totalWidth + 'px';
        }
        return '0';
    }
    finishedWidth() {
        if (this.scanningMetrics
            && this.scanningMetrics.metrics
            && this.scanningMetrics.total
            && this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.SUCCESS]) {
            return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.SUCCESS] /
                this.scanningMetrics.total * this.totalWidth + 'px';
        }
        return '0';
    }
    runningWidth() {
        if (this.scanningMetrics
            && this.scanningMetrics.metrics
            && this.scanningMetrics.total
            && this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.RUNNING]) {
            return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.RUNNING] /
                this.scanningMetrics.total * this.totalWidth + 'px';
        }
        return '0';
    }
    scanNow(): void {
        if (this.onSubmitting) {
            return; // Aoid duplicated submitting
        }

        if (!this.scanAvailable) {
            return; // Aoid page hacking
        }

        this.onSubmitting = true;
        this.scanningService.manualScan()
             .pipe(finalize(() => this.onSubmitting = false))
            .subscribe(() => {
                this.translate.get("CONFIG.SCANNING.TRIGGER_SCAN_ALL_SUCCESS").subscribe((res: string) => {
                    this.errorHandler.info(res);
                });
                // reset metrics and then get new metrics
                this.scanningMetrics = null;
                this.getManualMetrics();
            }
            , error => {
                if (error && error.status && error.status === 412) {
                    this.translate.get("CONFIG.SCANNING.TRIGGER_SCAN_ALL_FAIL",
                        { error: '' + errorHandFn(error) }).subscribe((res: string) => {
                        this.errorHandler.error(res);
                    });
                } else {
                    this.errorHandler.error(error);
                }
            });
    }

    reset(cron): void {
        this.schedule = {
            schedule: {
              type: this.CronScheduleComponent.scheduleType,
              cron: cron
            }
        };
    }

    saveSchedule(cron: string): void {
        let schedule = this.schedule;
        if (schedule && schedule.schedule && schedule.schedule.type !== SCHEDULE_TYPE_NONE) {
          this.scanningService.putSchedule(this.CronScheduleComponent.scheduleType, cron)
          .subscribe(response => {
              this.translate
                .get("CONFIG.SAVE_SUCCESS")
                .subscribe((res) => {
                  this.errorHandler.info(res);
                  this.CronScheduleComponent.resetSchedule();
                });
              this.reset(cron);
            },
            error => {
              this.errorHandler.error(error);
            }
          );
        } else {
          this.scanningService.postSchedule(this.CronScheduleComponent.scheduleType, cron)
            .subscribe(response => {
              this.translate.get("CONFIG.SAVE_SUCCESS").subscribe((res) => {
                this.errorHandler.info(res);
                this.CronScheduleComponent.resetSchedule();
              });
              this.reset(cron);
            },
            error => {
              this.errorHandler.error(error);
            }
          );
        }
    }
  isError(status: string): boolean {
      return status === VULNERABILITY_SCAN_STATUS.ERROR;
  }
  isFinished(status: string): boolean {
    return status === VULNERABILITY_SCAN_STATUS.SUCCESS;
  }
  isInProgress(status: string): boolean {
    return status === VULNERABILITY_SCAN_STATUS.RUNNING;
  }
}
